package com.renfei.multimediatask.ui.task4;


import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.view.View;
import android.widget.TextView;

import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.renfei.multimediatask.R;
import com.renfei.multimediatask.adapter.VideoInfoAdapter;
import com.renfei.multimediatask.base.BaseActivity;
import com.renfei.multimediatask.bean.MenuInfo;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.ArrayList;

import butterknife.BindView;
import butterknife.OnClick;

/**
 * 准备一个MP4文件，分离音频和视频数据
 * <p>
 * MediaExtractor的作用是把音频和视频的数据进行分离。
 * <p>
 * <p>
 * 通过MediaExtractor分离MP4文件得到的aac音频文件是没有添加adts头的原数据，所以保存的aac文件是没法播放的，只有添加了adts头才能播放。
 */
public class MediaExtractorDemoActivity extends BaseActivity {
    private static final String TAG = MediaExtractorDemoActivity.class.getSimpleName();
    @BindView(R.id.tv_getVideo_info)
    TextView tvGetVideoInfo;
    @BindView(R.id.tv_extractor_audio_video)
    TextView tvExtractorAudioVideo;
    @BindView(R.id.rv_video_info)
    RecyclerView rvVideoInfo;

    private ArrayList<MenuInfo> menuInfoList;
    private VideoInfoAdapter videoInfoAdapter;


//    private String basePath = Environment.getExternalStorageDirectory().getPath() + "/demo/";

    private String resourcePath;// = basePath + "video.mp4";


    private static final String VIDEO_TYPE = "video/";

    private static final String AUDIO_TYPE = "audio/";


    /**
     * 由于获取到的视频轨道的mime为video/avc,所以知道视频编码格式为.h264,获取到的音频轨道的mime为audio/mp4a-latm，
     * 对应的音频编码格式为aac。
     */

    private String outputAudioFile;//= basePath + "test.aac";
    private String outputVideoFile;//= basePath + "test.mp4";


    @Override
    protected int getLayoutId() {
        return R.layout.act_mediaextractor_demo;
    }

    @Override
    protected void initListener() {


        resourcePath    = getParentFilePath() + "video.mp4";
        outputAudioFile = getParentFilePath() + "video_extractor.aac";
        outputVideoFile = getParentFilePath() + "video_extractor.h264";
        initMenuList();
    }


    private void initMenuList() {
        menuInfoList = new ArrayList<>();
        rvVideoInfo.setLayoutManager(new LinearLayoutManager(this));
        videoInfoAdapter = new VideoInfoAdapter(menuInfoList);
        rvVideoInfo.setAdapter(videoInfoAdapter);
    }


    @OnClick({R.id.tv_getVideo_info, R.id.tv_extractor_audio_video})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.tv_getVideo_info:
                getVideoInfo();
                break;
            case R.id.tv_extractor_audio_video:
                extractorVideo();
                break;
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void getVideoInfo() {
        File file = new File(resourcePath);
        if (!file.exists()) {
            showToast("当前视频资源不存在!");
            return;
        }
        readVideoInfoTask task = new readVideoInfoTask(new WeakReference<MediaExtractorDemoActivity>(this));
        task.execute();
    }


    private void extractorVideo() {
        File file = new File(resourcePath);
        if (!file.exists()) {
            showToast("当前视频资源不存在!");
            return;
        }

        File audioFile = new File(outputAudioFile);
        File videoFile = new File(outputVideoFile);
        try {
            if (audioFile.exists()) {
                audioFile.delete();

                audioFile.createNewFile();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (videoFile.exists()) {
                videoFile.delete();

                videoFile.createNewFile();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        MediaExtractorTask task = new MediaExtractorTask(new WeakReference<>(this));
        task.execute();

    }

    /**
     * 提取视频，音频的异步任务
     */
    public static class MediaExtractorTask extends AsyncTask<Void, Integer, Integer> {
        private WeakReference<MediaExtractorDemoActivity> weakReference;

        public MediaExtractorTask(WeakReference<MediaExtractorDemoActivity> weakReference) {
            this.weakReference = weakReference;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            weakReference.get().showLoadingDialog();
        }

        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
        @Override
        protected Integer doInBackground(Void... voids) {
            weakReference.get().loge(TAG, "MediaExtractorTask   doInBackground ");
            MediaExtractor mediaExtractor = new MediaExtractor();
            try {
                mediaExtractor.setDataSource(weakReference.get().resourcePath);
            } catch (IOException e) {
                weakReference.get().loge(TAG, "MediaExtractorTask   doInBackground   mediaExtractor.setDataSource exception  ");
                e.printStackTrace();
            }
            int trackCount = mediaExtractor.getTrackCount();

            for (int i = 0; i < trackCount; i++) {
                MediaFormat format = mediaExtractor.getTrackFormat(i);
                String mineType = format.getString(MediaFormat.KEY_MIME);
                int maxVideoBufferCount = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);//获取视频的输出缓存的最大大小

                if (mineType.startsWith(AUDIO_TYPE)) {
                    weakReference.get().loge(TAG,"MediaExtractorTask   doInBackground   parse audio ");

                    mediaExtractor.selectTrack(i);
                    weakReference.get().loge(TAG, "audio KEY_MIME============" + format.getString(MediaFormat.KEY_MIME) + "============");
                    weakReference.get().loge(TAG, "audio KEY_CHANNEL_COUNT===" + format.getInteger(MediaFormat.KEY_CHANNEL_COUNT) + "==");
                    weakReference.get().loge(TAG, "audio KEY_SAMPLE_RATE=====" + format.getInteger(MediaFormat.KEY_SAMPLE_RATE) + "====");
                    weakReference.get().loge(TAG, "audio KEY_DURATION========" + format.getLong(MediaFormat.KEY_DURATION) + "==========");
                    weakReference.get().loge(TAG, "audio getSampleFlags======" + mediaExtractor.getSampleFlags() + "===================");
                    weakReference.get().loge(TAG, "audio getSampleTime=======" + mediaExtractor.getSampleTime() + "====================");
                    weakReference.get().loge(TAG, "audio getSampleTrackIndex=" + mediaExtractor.getSampleTrackIndex() + "==============");

                    try {
                        ByteBuffer byteBuffer = ByteBuffer.allocate(maxVideoBufferCount);
                        FileOutputStream fe = new FileOutputStream(weakReference.get().outputAudioFile, true);
                        while (true){
                            int readSampleCount =  mediaExtractor.readSampleData(byteBuffer,0);
                            if (readSampleCount<0){
                                break;
                            }
                            byte[] buffer = new byte[readSampleCount];
                            byteBuffer.get(buffer);

                            /**
                             * 添加adts头
                             */
                            byte[] adtsData = new byte[readSampleCount + 7];
                            addADTStoPacket(adtsData, readSampleCount+7);
                            System.arraycopy(buffer, 0, adtsData, 7, readSampleCount);

                            fe.write(buffer);
                            byteBuffer.clear();
                            mediaExtractor.advance();
                        }
                        fe.flush();
                        fe.close();
                    } catch (FileNotFoundException e) {
                        weakReference.get().loge(TAG,"MediaExtractorTask   doInBackground   FileNotFoundException ");
                        e.printStackTrace();
                    } catch (IOException e) {
                        weakReference.get().loge(TAG,"MediaExtractorTask   doInBackground   IOException ");
                        e.printStackTrace();
                    }
                }

                if (mineType.startsWith(VIDEO_TYPE)) {
                    mediaExtractor.selectTrack(i);
                    weakReference.get().loge(TAG, "====video=====KEY_MIME============" + format.getString(MediaFormat.KEY_MIME) + "========");
                    weakReference.get().loge(TAG, "====video=====KEY_DURATION========" + format.getLong(MediaFormat.KEY_DURATION) + "======");
                    weakReference.get().loge(TAG, "====video=====KEY_WIDTH===========" + format.getInteger(MediaFormat.KEY_WIDTH) + "======");
                    weakReference.get().loge(TAG, "====video=====KEY_HEIGHT==========" + format.getInteger(MediaFormat.KEY_HEIGHT) + "=====");
                    weakReference.get().loge(TAG, "====video=====getSampleRate=======" + format.getInteger(MediaFormat.KEY_FRAME_RATE) + "=");
                    weakReference.get().loge(TAG, "====video=====getSampleFlags======" + mediaExtractor.getSampleFlags() + "===============");
                    weakReference.get().loge(TAG, "====video=====getSampleTime=======" + mediaExtractor.getSampleTime() + "================");
                    weakReference.get().loge(TAG, "====video=====getSampleTrackIndex=" + mediaExtractor.getSampleTrackIndex() + "==========");


                    try {
                        MediaMuxer mediaMuxer = new MediaMuxer(weakReference.get().outputVideoFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
                        //给mediaMuxer 添加视频通道
                        int videoTrackIndex = mediaMuxer.addTrack(format);
                        mediaMuxer.start();

                        weakReference.get().loge(TAG, "MediaExtractorTask   doInBackground   MediaMuxer start  ");

                        //获取原视频的视频帧率
                        int framerate = format.getInteger(MediaFormat.KEY_FRAME_RATE);


                        //创建一个用来读取音频数据的buffer
                        ByteBuffer readBuffer = ByteBuffer.allocate(maxVideoBufferCount);
                        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
                        info.presentationTimeUs = 0;

                        /**
                         * writeSampleData(int trackIndex, @NonNull ByteBuffer byteBuf,@NonNull BufferInfo bufferInfo)
                         * MediaMuxer 写入数据
                         *
                         * trackIndex 是我们addTrack  返回的ID
                         * byteBuf便是要写入的数据(最好是一帧数据)
                         * 而bufferInfo是跟这一帧byteBuf相关的信息，包括时间戳、数据长度和数据在ByteBuffer中的位移
                         */

                        int sampleSize = 0;

                        while ((sampleSize = mediaExtractor.readSampleData(readBuffer, 0)) > 0) {
                            info.offset = 0;
                            info.size = sampleSize;
                            info.flags = MediaCodec.BUFFER_FLAG_SYNC_FRAME;
                            info.presentationTimeUs += 1000 * 1000 / framerate;
                            weakReference.get().loge(TAG, "MediaExtractorTask   doInBackground   MediaMuxer writeSampleData  ");
                            mediaMuxer.writeSampleData(videoTrackIndex, readBuffer, info);
                            mediaExtractor.advance();
                        }


                        mediaExtractor.release();

                        mediaMuxer.stop();
                        mediaMuxer.release();
                    } catch (IOException e) {
                        weakReference.get().loge(TAG, "MediaExtractorTask   doInBackground  MediaMuxer  IOException  ");
                        e.printStackTrace();
                    }
                    break;
                }
            }
            return 0;
        }

        /**
         * 通过MediaExtractor分离MP4文件得到的aac音频文件是没有添加adts头的原数据，所以保存的aac文件是没法播放的，只有添加了adts头才能播放。
         * @param packet
         * @param packetLen
         */
        private void addADTStoPacket(byte[] packet, int packetLen) {
 /*
        标识使用AAC级别 当前选择的是LC
        一共有1: AAC Main 2:AAC LC (Low Complexity) 3:AAC SSR (Scalable Sample Rate) 4:AAC LTP (Long Term Prediction)
        */
            int profile = 2;
            int frequencyIndex = 0x04; //设置采样率
            int channelConfiguration = 2; //设置频道,其实就是声道

            // fill in ADTS data
            packet[0] = (byte) 0xFF;
            packet[1] = (byte) 0xF9;
            packet[2] = (byte) (((profile - 1) << 6) + (frequencyIndex << 2) + (channelConfiguration >> 2));
            packet[3] = (byte) (((channelConfiguration & 3) << 6) + (packetLen >> 11));
            packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
            packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
            packet[6] = (byte) 0xFC;
        }

        @Override
        protected void onPostExecute(Integer integer) {
            super.onPostExecute(integer);
            weakReference.get().loge(TAG, "MediaExtractorTask   onPostExecute ");
            weakReference.get().dismissDialog();
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
            weakReference.get().dismissDialog();
        }

    }


    public static class readVideoInfoTask extends AsyncTask<Void, Integer, Integer> {

        private WeakReference<MediaExtractorDemoActivity> weakReference;

        public readVideoInfoTask(WeakReference<MediaExtractorDemoActivity> weakReference) {
            this.weakReference = weakReference;
        }

        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
        @Override
        protected Integer doInBackground(Void... voids) {

            try {
                MediaExtractor mediaExtractor = new MediaExtractor();
                mediaExtractor.setDataSource(weakReference.get().resourcePath);
                int trackCount = mediaExtractor.getTrackCount();


                int videoTrackIndex = 0;
                int audioTrackIndex = 0;


                for (int i = 0; i < trackCount; i++) {
                    MediaFormat mediaFormat = mediaExtractor.getTrackFormat(i);
                    String currentTrack = mediaFormat.getString(MediaFormat.KEY_MIME);

                    //获取视频通道index
                    if (currentTrack.startsWith(VIDEO_TYPE)) {
                        videoTrackIndex = i;
                    } else if (currentTrack.startsWith(AUDIO_TYPE)) {
                        //获取音频通道index
                        audioTrackIndex = i;
                    }
                }


                MediaFormat videoFormat = mediaExtractor.getTrackFormat(videoTrackIndex);
                MediaFormat audioFormat = mediaExtractor.getTrackFormat(audioTrackIndex);


                //1.获取MIME信息
                String videoFormatStr = videoFormat.getString(MediaFormat.KEY_MIME);//获取MIME格式内容
                String audioFormatStr = audioFormat.getString(MediaFormat.KEY_MIME);//获取MIME格式内容

                weakReference.get().loge(TAG, "MINE信息   " + videoFormatStr);
                weakReference.get().loge(TAG, "MINE信息   " + audioFormatStr);

                weakReference.get().menuInfoList.add(new MenuInfo("VIDEO格式内容", "" + videoFormatStr));
                weakReference.get().menuInfoList.add(new MenuInfo("AUDIO格式内容", "" + audioFormatStr));

                //2.获取语言格式
//                weakReference.get().menuInfoList.add(new MenuInfo("视频语言格式", "" + videoFormat.getString(MediaFormat.KEY_LANGUAGE)));
//                weakReference.get().menuInfoList.add(new MenuInfo("音频语言格式", "" + audioFormat.getString(MediaFormat.KEY_LANGUAGE)));

                //3.视频的高度与宽度
                weakReference.get().menuInfoList.add(new MenuInfo("视频宽度", "" + videoFormat.getInteger(MediaFormat.KEY_WIDTH)));
                weakReference.get().menuInfoList.add(new MenuInfo("视频高度", "" + videoFormat.getInteger(MediaFormat.KEY_HEIGHT)));

                //4.播放总时长
                weakReference.get().menuInfoList.add(new MenuInfo("播放总时长", "" + videoFormat.getLong(MediaFormat.KEY_DURATION)));

                //5.获取MediaFormat描述的数据缓冲区的最大字节数的键
                weakReference.get().menuInfoList.add(new MenuInfo("视频缓存输出的最大大小", "" + videoFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE)));


                String menuName1 = "获取采样率     ";
                String menuName2 = "获取比特       ";
                String menuName3 = "获取声道数量 ";
                String menuName4 = "最大宽度    ";
                String menuName5 = "最大高度    ";
                String menuName6 = "颜色格式    ";
                String menuName7 = "帧率    ";
                String menuName8 = "图块分辨率_宽 ";
                String menuName9 = "图块分辨率_高 ";
                String menuNamea = "网格行 ";
                String menuNameb = "网格列 ";
                String menuNamec = "PCM-编码 模拟信号编码 ";


                String menuValue1 = videoFormat.containsKey(MediaFormat.KEY_SAMPLE_RATE) ? "" + videoFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE) : "";
                String menuValue2 = videoFormat.containsKey(MediaFormat.KEY_BIT_RATE) ? "" + videoFormat.getInteger(MediaFormat.KEY_BIT_RATE) : "";
                String menuValue3 = videoFormat.containsKey(MediaFormat.KEY_CHANNEL_COUNT) ? "" + videoFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) : "";
                String menuValue4 = videoFormat.containsKey(MediaFormat.KEY_MAX_WIDTH) ? "" + videoFormat.getInteger(MediaFormat.KEY_MAX_WIDTH) : "";
                String menuValue5 = videoFormat.containsKey(MediaFormat.KEY_MAX_HEIGHT) ? "" + videoFormat.getInteger(MediaFormat.KEY_MAX_HEIGHT) : "";
                String menuValue6 = videoFormat.containsKey(MediaFormat.KEY_COLOR_FORMAT) ? "" + videoFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT) : "";
                String menuValue7 = videoFormat.containsKey(MediaFormat.KEY_FRAME_RATE) ? "" + videoFormat.getInteger(MediaFormat.KEY_FRAME_RATE) : "";
                String menuValue8 = videoFormat.containsKey(MediaFormat.KEY_TILE_WIDTH) ? "" + videoFormat.getInteger(MediaFormat.KEY_TILE_WIDTH) : "";
                String menuValue9 = videoFormat.containsKey(MediaFormat.KEY_TILE_HEIGHT) ? "" + videoFormat.getInteger(MediaFormat.KEY_TILE_HEIGHT) : "";
                String menuValuea = videoFormat.containsKey(MediaFormat.KEY_GRID_ROWS) ? "" + videoFormat.getInteger(MediaFormat.KEY_GRID_ROWS) : "";
                String menuValueb = videoFormat.containsKey(MediaFormat.KEY_GRID_COLUMNS) ? "" + videoFormat.getInteger(MediaFormat.KEY_GRID_COLUMNS) : "";
                String menuValuec = videoFormat.containsKey(MediaFormat.KEY_PCM_ENCODING) ? "" + videoFormat.getInteger(MediaFormat.KEY_PCM_ENCODING) : "";


                weakReference.get().menuInfoList.add(new MenuInfo(menuName1, menuValue1));
                weakReference.get().menuInfoList.add(new MenuInfo(menuName2, menuValue2));
                weakReference.get().menuInfoList.add(new MenuInfo(menuName3, menuValue3));
                weakReference.get().menuInfoList.add(new MenuInfo(menuName4, menuValue4));
                weakReference.get().menuInfoList.add(new MenuInfo(menuName5, menuValue5));
                weakReference.get().menuInfoList.add(new MenuInfo(menuName6, menuValue6));
                weakReference.get().menuInfoList.add(new MenuInfo(menuName7, menuValue7));
                weakReference.get().menuInfoList.add(new MenuInfo(menuName8, menuValue8));
                weakReference.get().menuInfoList.add(new MenuInfo(menuName9, menuValue9));
                weakReference.get().menuInfoList.add(new MenuInfo(menuNamea, menuValuea));
                weakReference.get().menuInfoList.add(new MenuInfo(menuNameb, menuValueb));
                weakReference.get().menuInfoList.add(new MenuInfo(menuNamec, menuValuec));


                mediaExtractor.release();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return 0;
        }

        @Override
        protected void onPostExecute(Integer integer) {
            super.onPostExecute(integer);

            weakReference.get().loge(TAG, "onPostExecute " + weakReference.get().menuInfoList.size());
            weakReference.get().videoInfoAdapter.setNewData(weakReference.get().menuInfoList);
            weakReference.get().videoInfoAdapter.notifyDataSetChanged();

        }
    }


}
